#include <stdio.h>
#include <sys/types.h>


	#ifdef TIME_WITH_SYS_TIME
		#include <sys/time.h>
		#include <time.h>
	#else
		#ifdef TM_IN_SYS_TIME
			#include <sys/time.h>
		#else
			#include <time.h>
		#endif
	#endif
	#include <sys/socket.h>


#include <stdlib.h>
#include <signal.h>
#include <math.h>
#include <errno.h>
//Zakomentovat v originalu a skusit prelozit - Kde to zarve?
#ifdef HAVE_FLOAT_H
#include <float.h>
#endif
//Konec zakomentovani

#ifndef __USE_GNU
	#define __USE_GNU
#endif

#include <string.h>

//Zakomentovat v originalu a skusit prelozit - Kde to zarve?
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
//Konec zakomentovani

#ifdef __EMX__
#define INCL_DOS
#include <os2.h>
#endif

//#include "data.h"
#include "server.h"
#include "net.h"
//#include "cfg.h"
//#include "hash.h"
//#include "time.h"
//#include "getopt.h"
//#include "math.h"
//#include "error.h"


unsigned short port=DEFAULT_PORT;  /* game port (not a gameport ;-) )*/
//int level_number=0;   /* line number in the LEVEL_FILE */
int fd;   /* socket */
//int n_players;   /* highest ID of player */
//int active_players=0;  /* # of players in the game */
//unsigned int id=0;  /* my ID */
//unsigned long_long game_start; /* time of game start */
/* important sprites */
//int grenade_sprite,bullet_sprite,slug_sprite,shell_sprite,shotgun_shell_sprite,mess1_sprite,mess2_sprite,mess3_sprite,mess4_sprite,noise_sprite;
//int shrapnel_sprite[N_SHRAPNELS];
//int nonquitable=0;  /* 1=clients can't abort game pressing F12 (request is ignored) */
unsigned long last_tick;
unsigned long last_player_left=0,last_packet_came=0;

//unsigned char *level_checksum=0;   /* MD5 sum of the level */

//unsigned char weapons_order[ARMS]={4,0,3,1,2};

/*struct birthplace_type
{
	int x,y;
}*birthplace=DUMMY;

int n_birthplaces=0;
*/



/* time queue of respawning objects 
struct queue_list
{
	struct queue_list* next;
	unsigned long_long time;
	struct it member;
}time_queue;
*/

/* list of objects 
struct object_list objects;

struct player_list *last_player;
struct object_list *last_obj;
*/




void print_ip(unsigned char * txt,struct in_addr ip)
{
	unsigned int a;

	sprintf(txt,"%d",*((unsigned char *)&ip));
	for (a=1;a<sizeof(ip);a++)
		sprintf(txt+strlen(txt),".%d",((unsigned char *)&ip)[a]);
}


/* write a message to stdout or stderr */
void message(unsigned char *msg,int output)
{
	time_t t;
	struct tm tm;
	static unsigned char timestamp[64];

#ifdef WIN32
	if ( !consoleApp )
		return;
#endif
	
	t=time(0);
	tm=*(localtime(&t));
	
	/* time stamp is in format day.month.year hour:minute:second */
	snprintf(timestamp,64,"%2d.%2d.%d %02d:%02d:%02d  ",tm.tm_mday,tm.tm_mon+1,tm.tm_year+1900,tm.tm_hour,tm.tm_min,tm.tm_sec);
	switch (output)
	{
		case 1:
		printf("%s%s",timestamp,msg);
		fflush(stdout);
		break;

		case 2:
		fprintf(stderr,"%s%s",timestamp,msg);
		fflush(stderr);
		break;
	}
}





/* initialize socket */
void init_socket(void)
{
	printf ("NOOB\n");
	message ("SUX", 2);
	struct sockaddr_in server;
	fd=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
	if (fd<0)
	{
		//ERROR("Error: Can't create socket!\n");
		//EXIT(1);
		message ("SUCKASS! THE NOOBZORING SOCKET!",2);
		//exit(2);
	}

	server.sin_family=AF_INET;
	server.sin_port=htons(port);
	server.sin_addr.s_addr=INADDR_ANY;
	message ("initalizing socket", 2);
	if (bind(fd,(struct sockaddr*)&server,sizeof(server)))
	{
		unsigned char msg[256];
		snprintf(msg,256,"Error: Can't socket to port %d!\n",port);
		message("FUCK!",2);
		//ERROR(msg);
		//EXIT(1);
		return;
	}
}



/*void send_chuck_to_player(struct player *player)
{
	static unsigned char p[MAX_PACKET_LENGTH];

	if (!(player->packet_pos))return;
	if (player->packet_pos>MAX_PACKET_LENGTH-1)return;
	p[0]=P_CHUNK;
	memcpy(p+1,player->packet,player->packet_pos);
	send_packet(p,(player->packet_pos)+1,(struct sockaddr*)(&(player->address)),0,player->id);
	player->packet_pos=0;
}
*/

/*void send_chuck_packet_to_player(unsigned char *packet, int len, struct player *player)
{
	if (len>MAX_PACKET_LENGTH-1)return;
	if ((player->packet_pos)+len>MAX_PACKET_LENGTH-1)send_chuck_to_player(player);
	memcpy((player->packet)+(player->packet_pos),packet,len);
	player->packet_pos+=len;
}
*/

void send_chucks(void)
{/*
	struct player_list* p;

	for (p=&players;p->next;p=p->next)
 		send_chuck_to_player(&(p->next->member));*/
}


/* send a packet to all players except one */
/* if not_this_player is null, sends to all players */
void sendall(unsigned char *packet,int len, struct player * not_this_player)
{/*
	struct player_list* p;

	if (!not_this_player)
		for (p=&players;p->next;p=p->next)
 			send_packet(packet,len,(struct sockaddr*)(&(p->next->member.address)),0,p->next->member.id);
	else
		for (p=&players;p->next;p=p->next)
			if ((&(p->next->member))!=not_this_player)
 				send_packet(packet,len,(struct sockaddr*)(&(p->next->member.address)),0,p->next->member.id);
*/}


/* send a packet to all players except one */
/* if not_this_player is null, sends to all players */
void sendall_chucked(unsigned char *packet,int len, struct player * not_this_player)
{/*
	struct player_list* p;

	if (!not_this_player)
		for (p=&players;p->next;p=p->next)
 			send_chuck_packet_to_player(packet,len,&(p->next->member));
	else
		for (p=&players;p->next;p=p->next)
			if ((&(p->next->member))!=not_this_player)
 				send_chuck_packet_to_player(packet,len,&(p->next->member));
*/}




/* send update status packet to all players (except not_this_player if this is !NULL)*/
void sendall_update_status(struct it* obj, struct player * not_this_player)
{/*
	struct player_list* p;
	static unsigned char packet[8];

	packet[0]=P_UPDATE_STATUS;
	//put_int(packet+1,obj->id);
	//put_int16(packet+5,obj->status);
	
	if (!not_this_player)
		for (p=&players;p->next;p=p->next)
 			send_chuck_packet_to_player(packet,7,&(p->next->member));
	else
		for (p=&players;p->next;p=p->next)
			if ((&(p->next->member))!=not_this_player)
 				send_chuck_packet_to_player(packet,7,&(p->next->member));
*/}



/* send message to a player */
/* name is name of player who sent the message (chat), NULL means it's from server */
void send_message(struct player* player,unsigned char *name,unsigned char *msg)
{/*
	static unsigned char packet[256];
	int len;

	packet[0]=P_MESSAGE;
	if (!name){snprintf(packet+1,256,"%s",msg);len=strlen(msg)+1+1;}
	else {snprintf(packet+1,256,"%s> %s",name,msg);len=strlen(name)+strlen(msg)+1+3;}
	send_chuck_packet_to_player(packet,len,player);
*/}

void sendall_bell(void)
{/*
	static unsigned char packet;
	struct player_list* p;

	packet=P_BELL;
	for (p=&players;p->next;p=p->next)
 		send_chuck_packet_to_player(&packet,1,&(p->next->member));

*/}




// read packet from socket 
void read_data(void)
{
	unsigned char txt[256];
	unsigned char txt1[256];
	fd_set rfds;
	struct timeval tv;
	struct sockaddr_in client;
	int a=sizeof(client);
	static unsigned char packet[280];
//	struct player *p;
//	struct player_list *q;
	int n_players = 0;
	unsigned char min,maj;
	int s,x,y,l;

	packet[279]=0;

	tv.tv_sec=0;
	tv.tv_usec=0;
	FD_ZERO(&rfds);
	FD_SET(fd,&rfds);
	while (select(fd+1,&rfds,0,0,&tv))
	{
		if ((l=recv_packet(packet,256,(struct sockaddr*)(&client),&a,0,0,&s))==-1)
			return;

 		last_packet_came=get_time();
 
		switch(*packet)
		{	
			case P_FUCK:
				message ("FUCK!!!!!!!!!!!!!!! FUCK YOU ALL!", 2);
				break;
			case P_NEW_PLAYER:
			if (l<6)break;   // invalid packet 
			{
				unsigned long t;
				if (packet[1]) // this byte must be always 0 - version authentification, old versions transmitted color byte here 
				{
					message("Incompatible client version.\n",2);
					packet[0]=P_PLAYER_REFUSED;
					packet[1]=42;
					//send_packet(packet,2,(struct sockaddr*)(&client),0,last_player->member.id);
					break;
				}
				maj=packet[2];
				min=packet[3];
				print_ip(txt1,client.sin_addr);
				snprintf(txt,256,"Request for player #%d (client version %d.%d) from %s.\n",n_players,maj,min,txt1);
				message(txt,2);
				if (maj!=VERSION_MAJOR||min<MIN_CLIENT_VERSION_MINOR)
				{
					message("Incompatible client version. Player refused.\n",2);
					packet[0]=P_PLAYER_REFUSED;
					packet[1]=42;
//					send_packet(packet,2,(struct sockaddr*)(&client),0,last_player->member.id);
					break;
				}
				//find_birthplace(&x,&y);
				/*if (add_player(packet[4],packet+5,&client,x,y)) // failed to add player
				{
					message("Player refused.\n",2);
					packet[0]=P_PLAYER_REFUSED;
					packet[1]=42;
//					send_packet(packet,2,(struct sockaddr*)(&client),0,last_player->member.id);
					break;
				}*/
				snprintf(txt,256,"Player #%d accepted, name \"%s\", address %s.\n",n_players,packet+5,txt1);
				message(txt,2);
				snprintf(txt,256,"%s entered the game.",packet+5);
				/*active_players++;
				n_players++;
				packet[0]=P_PLAYER_ACCEPTED;
				last_player->member.obj->status|=1024;
				put_int(packet+1,last_player->member.obj->id);
				put_int16(packet+5,last_player->member.obj->sprite);
				put_float(packet+7,last_player->member.obj->x);
				put_float(packet+11,last_player->member.obj->y);
				put_float(packet+15,last_player->member.obj->xspeed);
				put_float(packet+19,last_player->member.obj->yspeed);
				put_int16(packet+23,last_player->member.obj->status);
				t=get_time();
				put_long_long(packet+25,t-game_start);
				put_int(packet+33,last_player->member.id);
				packet[37]=VERSION_MAJOR;
				packet[38]=VERSION_MINOR;
				send_packet(packet,39,(struct sockaddr*)(&client),0,0);
				send_change_level(&(last_player->member));
				sendall_bell();
				sendall_message(0,txt,0,0);
				snprintf(txt,256,"There'%s %d %s in the game.",active_players==1?"s":"re",active_players,active_players==1?"player":"players");
				sendall_message(0,txt,0,0);
				send_info(0,0);*/
			}
			break;

			case P_LEVEL_ACCEPTED:
			/*q= find_player(&client,s);
			if (!q)break;
			if (q->member.current_level>=0)break;
			
			q->member.current_level=level_number;
			sendall_new_object(q->member.obj,&(q->member));
			create_noise(double2int(q->member.obj->x),double2int(q->member.obj->y),&(q->member));
			// send all objects in the game 
			send_objects(&(q->member),q->member.obj);
			//send all objects waiting for respawn 
			send_timeq_objects(&(q->member));
			break;

			case P_INFO:
			q=find_player(&client,s);
			if (!q) send_info((struct sockaddr*)(&client),0);
			else send_info((struct sockaddr*)(&client),q->member.id);
*/
			break;

			case P_REENTER_GAME:
			/*q=find_player(&client,s);
			if (!q)break;
			if (!(q->member.obj->status&1024)||(q->member.obj->status&4096))break;
			find_birthplace(&x,&y);
			create_noise(x,y,&(q->member));
			q->member.obj->x=int2double(x);
			q->member.obj->y=int2double(y);
			sendall_update_object(q->member.obj,0,3);  // update coordinates
*/ 
			break;
			
			case P_END:/*
			if (nonquitable)break;
			q=find_player(&client,s);
			if (!q)break;
			p=&(q->member);
			packet[0]=P_END;
			memcpy(packet+1,p->name,strlen(p->name)+1);
			sendall(packet,2+strlen(p->name),0);
*/
			snprintf(txt,256,"Game terminated"); //by player \"%s\".\n",p->name);
			message(txt,2);
			exit(0);

			case P_QUIT_REQUEST:/*
			q=find_player(&client,s);
			if (!q)  // this player has been deleted, but due to network inconsistency he doesn't know it 
			{
				packet[0]=P_PLAYER_DELETED;
				send_packet(packet,1,(struct sockaddr*)(&client),0,last_player->member.id);
				break;
			}
			snprintf(txt,256,"%s left the game.\n",q->member.name);
			message(txt,2);
			packet[0]=P_PLAYER_DELETED;
			send_packet(packet,1,(struct sockaddr*)(&client),0,last_player->member.id);
			snprintf(txt,256,"%s left the game.",q->member.name);
			sendall_message(0,txt,0,0);
			delete_player(q);
			send_info(0,0);
*/
			break;
		
			case P_KEYBOARD:/*
			if (l<3)break;   // invalid packet 
			q=find_player(&client,s);
			if (!q)break;
			q->member.last_update=get_time();
			q->member.keyboard_status.right=(packet[1])&1;
			q->member.keyboard_status.left=((packet[1])>>1)&1;
			q->member.keyboard_status.jump=((packet[1])>>2)&1;
			q->member.keyboard_status.creep=((packet[1])>>3)&1;
			q->member.keyboard_status.speed=((packet[1])>>4)&1;
			q->member.keyboard_status.fire=((packet[1])>>5)&1;
			q->member.keyboard_status.down_ladder=((packet[1])>>6)&1;
			q->member.keyboard_status.weapon=packet[2];
*/
			break;
			
			case P_MESSAGE:/*
			if (l<2)break;   //invalid packet 
			q=find_player(&client,s);
			if (!q)break;
			sendall_message(q->member.name,packet+1,0,0);
			snprintf(txt,256,"%s> %s\n",q->member.name,packet+1);
			message(txt,1);
*/
			break;

			default:
			snprintf(txt,256,"Unknown packet: head=%d\n",*packet);
			message(txt,2);
			break;
		}
	}
}








void free_all_memory(void)
{
	struct queue_list *t;
	struct player_list *p;

	/* delete players */

	/* delete objects */
	//while (last_obj!=(&objects))delete_obj(last_obj->member.id);

	/*
	for (t=&time_queue;t->next;)
	{
		struct queue_list *q;

		q=t->next->next;
		mem_free(t->next);
		t->next=q;
	}
	*/

	/* delete birthplaces */
	//if (n_birthplaces)mem_free(birthplace);
	
	/* delete sprites */
	//free_sprites(0);
	
	//free_area();
	//if (level_checksum)mem_free(level_checksum);
}

/* fatal signal handler (sigsegv, sigabrt, ... ) */
void signal_handler(int sig_num)
{
	unsigned char packet[16];
	unsigned char txt[256];
	
	packet[0]=P_END;
	memcpy(packet+1,"server",7);

	sendall(packet,8,0);
	sendall(packet,8,0);
	sendall(packet,8,0);
	sendall(packet,8,0);
	sendall(packet,8,0);
	sendall(packet,8,0);
	sendall(packet,8,0);
	sendall(packet,8,0);
	/* 800 % redundancy should be enough ;-) */

	snprintf(txt,256,"Signal %d caught.\n",sig_num);
	message(txt,2);
	free_all_memory();
	
#ifdef WIN32
	hServerExitEvent=1;
#else
	signal(sig_num,SIG_DFL);
	raise(sig_num);
#endif
}







/*-----------------------------------------------------------------------------------*/
int server(void)
{
	int a;
	unsigned char txt[256];
	unsigned long last_time;
	//unsigned char *LEVEL;
	
	//last_player=&players;
	//last_obj=&objects;
	
	snprintf(txt,256,"Running 0verkill server version %d.%d\n",0,0);
	message(txt,2);

	message("Initialization.\n",2);

	
//	hash_table_init(); //hash table sucks


	message("Not loading sprites.\n",2);
	//load_sprites(DATA_PATH GAME_SPRITES_FILE); /* players, corpses, bullets, ... */
	//if (find_sprite("bullet",&bullet_sprite)){unsigned char msg[256];snprintf(msg,256,"Can't find sprite \"bullet\".\n");ERROR(msg);EXIT(1);}
/*	
if (find_sprite("slug",&slug_sprite)){unsigned char msg[256];snprintf(msg,256,"Can't find sprite \"slug\".\n");ERROR(msg);EXIT(1);}
	if (find_sprite("shell",&shell_sprite)){unsigned char msg[256];snprintf(msg,256,"Can't find sprite \"shell\".\n");ERROR(msg);EXIT(1);}
	if (find_sprite("sshell",&shotgun_shell_sprite)){unsigned char msg[256];snprintf(msg,256,"Can't find sprite \"sshell\".\n");ERROR(msg);EXIT(1);}
	if (find_sprite("grenade",&grenade_sprite)){unsigned char msg[256];snprintf(msg,256,"Can't find sprite \"grenade\".\n");ERROR(msg);EXIT(1);}
	if (find_sprite("mess1",&mess1_sprite)){unsigned char msg[256];snprintf(msg,256,"Can't find sprite \"mess1\".\n");ERROR(msg);EXIT(1);}
	if (find_sprite("mess2",&mess2_sprite)){unsigned char msg[256];snprintf(msg,256,"Can't find sprite \"mess2\".\n");ERROR(msg);EXIT(1);}
	if (find_sprite("mess3",&mess3_sprite)){unsigned char msg[256];snprintf(msg,256,"Can't find sprite \"mess3\".\n");ERROR(msg);EXIT(1);}
	if (find_sprite("mess4",&mess4_sprite)){unsigned char msg[256];snprintf(msg,256,"Can't find sprite \"mess4\".\n");ERROR(msg);EXIT(1);}
	if (find_sprite("noise",&noise_sprite)){unsigned char msg[256];snprintf(msg,256,"Can't find sprite \"noise\".\n");ERROR(msg);EXIT(1);}
	for (a=0;a<N_SHRAPNELS;a++)
	{
		sprintf(txt,"shrapnel%d",a+1);
		if (find_sprite(txt,&shrapnel_sprite[a])){unsigned char msg[256];snprintf(msg,256,"Can't find sprite \"%s\".\n",txt);ERROR(msg);EXIT(1);}
	}
*/	
	//LEVEL=load_level(level_number);
	//level_checksum=md5_level(level_number);
	//if (!LEVEL){char txt[256];snprintf(txt,256,"Can't load level number %d\n",level_number);ERROR(txt);EXIT(1);}
	//snprintf(txt,256,"Loading level \"%s\"....\n",LEVEL);
	//message(txt,2);
	//snprintf(txt,256,"%s%s%s",DATA_PATH,LEVEL,LEVEL_SPRITES_SUFFIX);
	message("Loading level graphics.\n",2);
	//load_sprites(txt);
	//snprintf(txt,256,"%s%s%s",DATA_PATH,LEVEL,STATIC_DATA_SUFFIX);
	message("Loading level map.\n",2);
	//load_data(txt);
	//snprintf(txt,256,"%s%s%s",DATA_PATH,LEVEL,DYNAMIC_DATA_SUFFIX);
	//message("Loading level objects.\n",2);
	//mem_free(LEVEL);
	//load_dynamic(txt);

	message("Initializing socket-dfgsuck.\n",2);
	init_socket();  /* initialize socket */
	
	message("Installing signal handlers.\n",2);
	signal(SIGINT,signal_handler);
	signal(SIGTERM,signal_handler);
	signal(SIGFPE,signal_handler);
	signal(SIGILL,signal_handler);
	signal(SIGABRT,signal_handler);

	message("Game started.\n",2);

#ifdef __EMX__
	DosSetPriority(PRTYS_PROCESS, PRTYC_FOREGROUNDSERVER, 1, 0);
#endif
	
	//game_start=get_time();
	//srandom(game_start);


	last_time=get_time();
	while(1) {
		last_time+=PERIOD_USEC;
		if (get_time()-last_time>PERIOD_USEC*100)last_time=get_time();
		read_data();
//		update_timeq();
//		update_game();
//		update_players();    /* MUST come after update_game otherwise when player shoots he hit himself */
		send_chucks();
		last_tick=get_time();
		/*if (!active_players &&(last_tick-last_player_left)>DELAY_BEFORE_SLEEP_USEC &&(last_tick-last_packet_came)>DELAY_BEFORE_SLEEP_USEC)
		{	

		WaitForSingleObject(fd, 500); /* wait max. 0.5 seconds, then we must test hServerExitEvent 
	
		}*/

		sleep_until(last_time+PERIOD_USEC);
 	}
 
	return 0;
}


int main(int argc, char **argv)
{
	int a;



	
	a=server();
	//free_all_memory();
	
	return a;
}
